/*
 * Copyright (C) 2011-2012 Adam Gregoire <bsdunx@gmail.com>
 * Copyright (C) 2011-2012 Jeff Hubbard <lord2800@gmail.com>
 *
 * This file is part of the ScriptKit scripting engine library.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "config.hpp"

#ifdef WANT_CRT_DEBUG
#include "CrtDebug.hpp"

#undef THIS_FILE
static char THIS_FILE[] = __FILE__;

#define new DEBUG_NEW
#define malloc(x) MALLOC_DBG(x)

#endif

#ifdef OS_WIN32
#include <Windows.h>
#endif

#ifdef OS_WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif

#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif

#include <ctime>

#include <cstdint>
#include <iostream>

#ifdef HAVE_STL_THREAD
#include <thread>
#else
#include <boost/thread.hpp>
#endif

#include "jsapi.h"
#include "jsapi_ext.hpp"
#include "jsapi_ext_util.hpp"
#include "ScriptKit.hpp"
#include "SMKit.hpp"

using namespace ScriptKit;

JSAPI_FUNC(console_print);
JSAPI_FUNC(console_sleep);
JSAPI_FUNC(console_pause);

JSAPI_EMPTY_CTOR_BODY(Console)

static JSClass console =
{
	"Console", 0, JSCLASS_DEFAULT_WITH_CTOR(Console)
};

static JSFunctionSpec console_methods[] =
{
	JS_FS("print", console_print, 1, JSPROP_STATIC),
	JS_FS("sleep", console_sleep, 1, JSPROP_STATIC),
	JS_FS("pause", console_pause, 1, JSPROP_STATIC),
	JS_FS_END
};

static JSClassSpec console_classes[] =
{
	JS_CS(&console, nullptr, Console, 0, nullptr, nullptr, console_methods, nullptr),
	JS_CS_END
};

static JSModuleSpec console_mods[] =
{
	JS_MS("console", console_classes, nullptr, nullptr),
	JS_MS_END
};

JSAPI_FUNC(console_print)
{
	JSString* str = nullptr;
	if(!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S", &str))
		return JS_FALSE;

	const size_t len = JS_GetStringEncodingLength(cx, str);
	char* cstr = new char[len+1];
	memset(cstr, 0, len+1);
	size_t convcount = JS_EncodeStringToBuffer(str, cstr, len);
	if(convcount > len)
	{
		JS_free(cx, cstr);
		return JS_FALSE;
	}
	if(convcount == (size_t)-1)
		return JS_FALSE;

	std::cout << cstr << std::endl;

	JS_free(cx, cstr);

	JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_TRUE));
	return JS_TRUE;
}

JSAPI_FUNC(console_sleep)
{
	uint32_t amount = 1;
	if(!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "u", &amount))
		return JS_FALSE;

#ifdef OS_WIN32
	Sleep(amount);
#else
	timespec ts;
	ts.tv_sec = amount/1000;
	ts.tv_nsec = amount*1000000;
	nanosleep(&ts, nullptr);
#endif

	JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_TRUE));
	return JS_TRUE;
}

JSAPI_FUNC(console_pause)
{
	JSBool pause;
	if(!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "u", &pause))
		return JS_FALSE;

	JSRuntime* rt = JS_GetRuntime(cx);
	auto script = (SMKit::Script*)JS_GetContextPrivate(cx);
	if(pause == JS_TRUE)
	{
		script->state = State::PAUSED;
		JS_TriggerOperationCallback(rt);
	}
	else
	{
		if(script->state == State::PAUSED)
			script->state = State::EXECUTING;
	}

	JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_TRUE));
	return JS_TRUE;
}

int main()
{

#if defined _DEBUG && defined OS_WIN32 && defined _CRTDBG_MAP_ALLOC
	::_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
	::_CrtSetReportHook(&CrtDebugReportHook);
#endif

#ifdef OS_WIN32
	//HMODULE hModScriptKit = ::LoadLibraryExW(L"ScriptKit.dll", nullptr, 0);

	//FARPROC fpInitAddr = ::GetProcAddress(hModScriptKit, "Initialize");
	//FARPROC fpGetInstanceAddr = ::GetProcAddress(hModScriptKit, "GetInstance");

	// FIXME add error checking
	// GetError
#else /* POSIX */
	//void* hModScriptKit = ::dlopen("ScriptKit.so", RTLD_NOW);

	//void* fpInitAddr = ::dlsym(hModScriptKit, "Initialize");
	//void* fpGetInstanceAddr = ::dlsym(hModScriptKit, "GetInstance");

	// FIXME add error checking
	//dlerror();
#endif

#ifdef OS_WIN32
	char pathbuf[MAX_PATH] = { 0 };
	_getcwd(pathbuf, MAX_PATH);
#else
	char pathbuf[4096] = { 0 };
	getcwd(pathbuf, 4096); // FIXME
#endif
	std::string path(pathbuf);
	std::cout << path << std::endl;
	SMKit* smEngine = static_cast<SMKit*>(GetEngine(path));

	
	std::string str("test.js");

	smEngine->RegisterModuleDefinition(reinterpret_cast<IScript::Module::moduledef_t*>(console_mods));

	auto script = smEngine->CompileScript(str);
	if(!script)
	{
		std::cout << "Unable to compile script `" << str << "`" << std::endl;
	}

	script = nullptr;
	script = smEngine->GetScript(str);

	if(script)
	{
		bool success = smEngine->RunScript(script);
		if(success)
			script->thr.join();
	}

	smEngine->Shutdown();
}
